using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

namespace Life.UI.MVC
{
    public static class UIWaiterPool
    {
        private static System.Action<UIWaiter, bool> _uiWaiterInActiveSetter;
        private static readonly Dictionary<RuntimeTypeHandle, System.Action<object, bool>> _uiWaiterInActiveSetters = new ();
        static ObjectPool<UIWaiter> _waiterPool = new ObjectPool<UIWaiter>(CreateWaiter, OnUIWaiterGet,OnUIWaiterRelease);
        private static readonly Dictionary<RuntimeTypeHandle, object> _waiterPools =
            new Dictionary<RuntimeTypeHandle, object>();
        static UIWaiter CreateWaiter()
        {
            return new UIWaiter();
        }
        
        static UIWaiter<T> CreateWaiter<T>()
        {
            return new UIWaiter<T>();
        }
        static void OnUIWaiterGet(UIWaiter waiter)
        {
            _uiWaiterInActiveSetter(waiter, true);
            waiter.Reset();
        }
        static void OnUIWaiterGet<T>(UIWaiter<T> waiter)
        {
            var key = typeof(T).TypeHandle;
            if (_uiWaiterInActiveSetters.TryGetValue(key, out var setter))
            {
                ((Action<UIWaiter<T>,bool>)setter)?.Invoke(waiter, true);
                waiter.Reset();
            }
        }
        static void OnUIWaiterRelease(UIWaiter waiter)
        {
            _uiWaiterInActiveSetter(waiter, false);
            waiter.Reset();
        }

        static void OnUIWaiterRelease<T>(UIWaiter<T> waiter)
        {
            if (!waiter.InActive)
            {
                Debug.LogError("这是一个已存在池里的Waiter 或者 非池分配的Waiter");
                return;
            }
            var key = typeof(T).TypeHandle;
            if (_uiWaiterInActiveSetters.TryGetValue(key, out var setter))
            {
                ((Action<UIWaiter<T>,bool>)setter)?.Invoke(waiter, false);
                waiter.Reset();
            }
        }

        public static UIWaiter GetWaiter()
        {
            return _waiterPool.Get();
        }

        public static void ReleaseWaiter(UIWaiter waiter)
        {
            _waiterPool.Release(waiter);
        }
        
        public static UIWaiter<T> GetWaiter<T>()
        {
            if (!_waiterPools.TryGetValue(typeof(T).TypeHandle, out var pool))
            {
                pool = new ObjectPool<UIWaiter<T>>(CreateWaiter<T>, OnUIWaiterGet, OnUIWaiterRelease);
                _waiterPools.Add(typeof(T).TypeHandle, pool);
            }
            return ((ObjectPool<UIWaiter<T>>)pool).Get();
        }
        
        public static void ReleaseWaiter<T>(UIWaiter<T> waiter)
        {
            if (_waiterPools.TryGetValue(typeof(T).TypeHandle, out var pool))
            {
                ((ObjectPool<UIWaiter<T>>)pool).Release(waiter);
            }
            else
            {
                Debug.LogError($"没有找到类型为 {typeof(T)} 的UIWaiter池");
            }
        }

        public class UIWaiter : Life.UI.MVC.UIWaiter
        {
            public string Name { get; set; }
            static UIWaiter()
            {
                _uiWaiterInActiveSetter = SetInActive;
            }
            [Obsolete("请使用UIWaiterPool.GetWaiter()获取UIWaiter实例")]
            public UIWaiter()
            {
                
            }
            private static void SetInActive(UIWaiter waiter, bool inActive)
            {
                waiter.InActive = inActive;
                if (!inActive)
                {
                    waiter.Disbinding();
                }
#if UNITY_EDITOR
                waiter.WaiterId = DateTime.Now.Ticks;
#endif
            }
            public bool InActive { get; private set; }
#if UNITY_EDITOR
            public long WaiterId { get; private set; }
#endif

            public override bool keepWaiting
            {
                get
                {
                    if (!InActive)
                    {
                        Debug.LogWarning($"UIWaiter {Name} 已入池待复用，但依然有协程在等待\n如果使用了var handler = waiter.WaitAutoRelease();请 yield return handler;而不是 yield return waiter;\n已返回成功");
                        return false;
                    }

                    return base.keepWaiting;
                }
            }
            /// <summary>
            /// 回收Waiter
            /// </summary>
            public void Release()
            {
                ReleaseWaiter(this);
            }

            /// <summary>
            /// 非泛型 UIWaiter：等待完成后自动释放
            /// </summary>
            public AutoReleaseWaiterHandler WaitAutoRelease()
            {
                return this;
            }
        }

        public class UIWaiter<T> : Life.UI.MVC.UIWaiter<T>
        {
            public string Name { get; set; }
            static UIWaiter()
            {
                _uiWaiterInActiveSetters[typeof(T).TypeHandle] = SetInActive;
            }
            [Obsolete("请使用UIWaiterPool.GetWaiter<T>()获取UIWaiter实例")]
            public UIWaiter()
            {
                
            }
            private static void SetInActive(object waiterObj, bool inActive)
            {
                UIWaiter<T> waiter = (UIWaiter<T>)waiterObj;
                waiter.InActive = inActive;
                if (!inActive)
                {
                    waiter.Disbinding();
                }
#if UNITY_EDITOR
                waiter.WaiterId = DateTime.Now.Ticks;
#endif
            }
            
            public bool InActive { get; private set; }
#if UNITY_EDITOR
            public long WaiterId { get; private set; }
#endif
            public override bool keepWaiting
            {
                get
                {
                    if (!InActive)
                    {
                        Debug.LogWarning($"UIWaiter {Name} 已入池待复用，但依然有协程在等待\n如果使用了var handler = waiter.WaitAutoRelease();请 yield return handler;而不是 yield return waiter;\n已返回成功");
                        return false;
                    }

                    return base.keepWaiting;
                }
            }

            /// <summary>
            /// 回收Waiter并返回Waiter的结果值
            /// </summary>
            /// <returns></returns>
            public T Release()
            {
                T result = Value;
                ReleaseWaiter(this);
                return result;
            }

            /// <summary>
            /// 泛型 UIWaiter：等待完成后自动释放并返回结果
            /// </summary>
            public AutoReleaseWaiterHandler<T> WaitAutoRelease()
            {
                return this;
            }
        }
    }

    /// <summary>
    /// 自动释放的非泛型封装
    /// </summary>
    public class AutoReleaseWaiterHandler : CustomYieldInstruction
    {
        private bool _completed;
        public override bool keepWaiting => !_completed;

        public static implicit operator AutoReleaseWaiterHandler(UIWaiterPool.UIWaiter waiter)
        {
            if (waiter != null)
            {
                var handler = new AutoReleaseWaiterHandler();
                waiter.OnCompleteEvent += () =>
                {
                    waiter.Release();
                    handler._completed = true;
                };
                return handler;
            }
            return null;
        }
    }

    /// <summary>
    /// 自动释放的泛型封装，带返回值
    /// </summary>
    public class AutoReleaseWaiterHandler<T> : CustomYieldInstruction
    {
        private bool _completed;
        public override bool keepWaiting => !_completed;

        public T Result { get; private set; }
        /// <summary>
        /// 取走结果，并释放引用（如果T是Class的话）
        /// </summary>
        /// <returns></returns>
        public T TakeResult()
        {
            if (typeof(T).IsClass)
            {
                var temp = Result;
                Result = default;
                return temp;
            }
            return Result;
        }

        public static implicit operator AutoReleaseWaiterHandler<T>(UIWaiterPool.UIWaiter<T> waiter)
        {
            if (waiter != null)
            {
                var handler = new AutoReleaseWaiterHandler<T>();
                waiter.OnCompleteEvent += value =>
                {
                    handler.Result = waiter.Release();
                    handler._completed = true;
                };
                return handler;
            }
            return null;
        }
    }
}
